package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.aliyuncs.utils.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.feign.BehaviorFeign;
import com.heima.article.feign.UserFeign;
import com.heima.article.mapper.*;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojos.*;
import com.heima.model.article.vo.HotArticleVo;
import com.heima.model.behavior.pojos.ApBehaviorEntry;
import com.heima.model.behavior.pojos.ApLikesBehavior;
import com.heima.model.behavior.pojos.ApUnlikesBehavior;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;


@SuppressWarnings("ALL")
@Service
@Transactional
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private AuthorMapper authorMapper;
    //单页最大加载数
    private static final short MAX_PAGE_SIZE=50;
    @Value("${fdfs.url}")
    private String fileServerUrl;
    @Autowired
    private UserFeign userFeign;
    @Autowired
    private BehaviorFeign behaviorFeign;
    @Autowired
    private ApCollectionMapper apCollectionMapper;
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 保存或修改文章
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArtilce(ArticleDto articleDto) {
        //1. 检查参数
        if (articleDto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto,apArticle);
        //2 保存或修改文章
        if (articleDto.getId()==null){//保存
            //补全作者id
            ApAuthor apAuthor = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, apArticle.getAuthorName()));
            if (apAuthor!=null){
                apArticle.setAuthorId(apAuthor.getId().longValue());
            }
            //保存文章
            save(apArticle);
            //保存文章内容
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setContent(articleDto.getContent());
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContentMapper.insert(apArticleContent);
            //保存文章配置
            ApArticleConfig apArticleConfig = new ApArticleConfig();
            apArticleConfig.setIsForward(true);
            apArticleConfig.setIsComment(true);
            apArticleConfig.setIsDown(false);
            apArticleConfig.setIsDelete(false);
            apArticleConfig.setArticleId(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);
        }else {//修改
            ApArticle article = getById(apArticle.getId());
            if (article==null){
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章没找到");
            }
            //修改文章
            updateById(apArticle);
            //修改文章内容
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, apArticle.getId()));
            apArticleContent.setContent(articleDto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }

        //导入数据到es索引库
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", apArticle.getId().toString());
        map.put("publishTime", apArticle.getPublishTime());
        map.put("layout", apArticle.getLayout());
        map.put("images", apArticle.getImages());
        map.put("authorId", apArticle.getAuthorId());
        map.put("title", apArticle.getTitle());
        IndexRequest indexRequest = new IndexRequest("app_info_article").id(apArticle.getId().toString()).source(map);
        try {
            client.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return ResponseResult.okResult(apArticle.getId());
    }

    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        // 1 参数校验
        Integer size = dto.getSize();
        if (size==null||size==0){
            size=10;
        }
        size = Math.min(size,MAX_PAGE_SIZE);
        dto.setSize(size);
        //类型参数校验
        if (!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
            loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
        }
        //文章频道校验
        if (StringUtils.isEmpty(dto.getTag())){
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        //时间校验
        if (dto.getMaxBehotTime()==null) dto.setMaxBehotTime(new Date());
        if (dto.getMinBehotTime()==null) dto.setMinBehotTime(new Date());
        //2 查询参数
        List<ApArticle> apArticles = baseMapper.loadArticleList(dto, loadtype);
        //3 结果封装
        ResponseResult responseResult = ResponseResult.okResult(apArticles);
        responseResult.setHost(fileServerUrl);
        return responseResult;
    }

    /**
     * 加载文章详情
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArtilceInfo(ArticleInfoDto dto) {
        Map<String,Object> resultMap = new HashMap<>();
        //1 检查参数
        if (dto==null||dto.getArticleId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2 查询文章配置
        ApArticleConfig apArticleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if (apArticleConfig==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if (!apArticleConfig.getIsDelete()&&!apArticleConfig.getIsDown()){
            //3 查询文章内容
            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, dto.getArticleId()));
            resultMap.put("content",apArticleContent);
        }
        resultMap.put("config",apArticleConfig);
        //4 结果返回
        return ResponseResult.okResult(resultMap);
    }

    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        //1参数检查
        if (dto==null||dto.getArticleId()==null||dto.getAuthorId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2查询行为实体
        ApUser apUser = AppThreadLocalUtils.getUser();
        ApBehaviorEntry apBehaviorEntry = behaviorFeign.getBehaviorEntry(apUser.getId(), dto.getEquipmentId());
        if (apBehaviorEntry==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //{"isfollow":true,"islike":true,"isunlike":false,"iscollection":true}
        boolean isunlike=false,islike=false,iscollection=false,isfollow=false;
        //3 是否关注
        ApAuthor apAuthor = authorMapper.selectById(dto.getAuthorId());
        if (apAuthor!=null){
            boolean followStatus = userFeign.getFollowStatus(apUser.getId().longValue(), dto.getAuthorId().longValue());
            isfollow=followStatus;
        }
        //4 查询不喜欢行为
        ApUnlikesBehavior apUnlikesBehavior = behaviorFeign.findUnLikeByArticleIdAndEntryId(apBehaviorEntry.getId(), dto.getArticleId());
        if(apUnlikesBehavior!=null&&apUnlikesBehavior.getType().equals(ApUnlikesBehavior.Type.UNLIKE.getCode())){
            isunlike=true;
        }
        //5 查询点赞
        ApLikesBehavior apLikesBehavior = behaviorFeign.findLikeByArticleIdAndEntryId(apBehaviorEntry.getId(), dto.getArticleId(), ApLikesBehavior.Type.ARTICLE.getCode());
        if (apLikesBehavior!=null&&apLikesBehavior.getOperation().equals(ApLikesBehavior.Operation.LIKE.getCode())){
            islike=true;
        }
        //6 查询收藏
        ApCollection apCollection = apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                .eq(ApCollection::getArticleId, dto.getArticleId())
                .eq(ApCollection::getEntryId, apBehaviorEntry.getId()));
        if (apCollection!=null&&apCollection.getType()==0){
            iscollection=true;
        }
        //7.结果封装返回
        //{"isfollow":true,"islike":true,"isunlike":false,"iscollection":true}
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("isfollow",isfollow);
        resultMap.put("islike",islike);
        resultMap.put("isunlike",isunlike);
        resultMap.put("iscollection",iscollection);
        return ResponseResult.okResult(resultMap);
    }

    /**
     * 重新计算文章分值
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        //查询文章
        ApArticle apArticle = getById(mess.getArticleId());
        //2 修改文章
        apArticle.setViews((int)(apArticle.getViews()==null ? 0 : apArticle.getViews() +mess.getView()));
        apArticle.setCollection((int)(apArticle.getCollection()==null ? 0 : apArticle.getCollection() +mess.getCollect()));
        apArticle.setComment((int)(apArticle.getComment()==null ? 0 : apArticle.getComment() +mess.getComment()));
        apArticle.setLikes((int)(apArticle.getLikes()==null ? 0 : apArticle.getLikes() +mess.getLike()));
        updateById(apArticle);
        //3 计算文章分值
        Integer score = computeScore(apArticle);
        score=score*3;
         //4 查询redis
        String articleListStr = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        cacheAndToRedis(apArticle,score,articleListStr,articleListStr,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+apArticle.getChannelId());
        //5 更新推荐缓存中的数据
        String articleListAllStr = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
        cacheAndToRedis(apArticle,score,articleListStr,articleListAllStr,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);
    }

    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if (firstPage){
            String jsonStr = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StringUtils.isNotEmpty(jsonStr)){
                List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);
                if (!hotArticleVoList.isEmpty() && hotArticleVoList.size()>0){
                    ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                    responseResult.setHost(fileServerUrl);
                    return responseResult;
                }
            }
        }
        return load(loadtypeLoadMore,dto);
    }

    /**
     * 重新计算文章分析，并替换
     * @param apArticle
     * @param score
     * @param articleListStr
     * @param articleListAllStr
     * @param s
     */
    private void cacheAndToRedis(ApArticle apArticle, Integer score, String articleListStr, String articleListAllStr, String s) {
        if (StringUtils.isNotEmpty(articleListAllStr)){
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(articleListStr, HotArticleVo.class);
            boolean flag = true;
            //如果当前缓存中没有当前文章，更新分值
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if (hotArticleVo.getId().equals(apArticle.getId())){
                    hotArticleVo.setScore(score);
                    flag = false;
                    break;
                }
            }
            //缓存中没有当前文章，获取缓存中的最小分值的文章作对比，-->替换
            if (flag && hotArticleVoList.size() >= 30) {
                hotArticleVoList.sort(new Comparator<HotArticleVo>() {
                    @Override
                    public int compare(HotArticleVo o1, HotArticleVo o2) {
                        return o2.getScore().compareTo(o1.getScore());
                    }
                });
                //找到分值最小的文章
                HotArticleVo hotArticleVo = hotArticleVoList.get(hotArticleVoList.size() - 1);
                if (hotArticleVo.getScore() < score) {
                    hotArticleVoList.remove(hotArticleVo);
                    HotArticleVo hot = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hot);
                    hot.setScore(score);
                    hotArticleVoList.add(hot);
                }
            } else if(flag) {
                HotArticleVo hot = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, hot);
                hot.setScore(score);
                hotArticleVoList.add(hot);
            }
            //更新缓存中的数据
            redisTemplate.opsForValue().set(s, JSON.toJSONString(hotArticleVoList));
        }
    }

    /**
     * 计算某一个文章的分值
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        if (apArticle.getLikes()!=null){
            score+=apArticle.getLikes()*ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getViews()!=null){
            score+=apArticle.getViews();
        }
        if (apArticle.getComment()!=null){
            score+=apArticle.getComment()*ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getCollection()!=null){
            score+=apArticle.getCollection()*ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}